/*
 * Copyright 2017-Present, Redis Ltd. and Contributors
 * All rights reserved.
 *
 * Licensed under the MIT License.
 *
 * This file contains contributions from third-party contributors
 * licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.lettuce.core.api.coroutines

import io.lettuce.core.CompareCondition
import java.util.Date
import java.time.Instant
import java.time.Duration

import io.lettuce.core.CopyArgs
import io.lettuce.core.ExpireArgs
import io.lettuce.core.KeyScanArgs
import io.lettuce.core.KeyScanCursor
import io.lettuce.core.MigrateArgs
import io.lettuce.core.RestoreArgs
import io.lettuce.core.ScanArgs
import io.lettuce.core.ScanCursor
import io.lettuce.core.SortArgs
import io.lettuce.core.ExperimentalLettuceCoroutinesApi
import io.lettuce.core.annotations.Experimental
import kotlinx.coroutines.flow.Flow

/**
 * Coroutine executed commands for Keys (Key manipulation/querying).
 *
 * @param <K> Key type.
 * @param <V> Value type.
 * @author Mikhael Sokolov
 * @since 6.0
 * @generated by io.lettuce.apigenerator.CreateKotlinCoroutinesApi
 */
@ExperimentalLettuceCoroutinesApi
interface RedisKeyCoroutinesCommands<K : Any, V : Any> {

    /**
     * Copy the value stored at the source key to the destination key.
     *
     * @param source the source.
     * @param destination the destination.
     * @return Boolean integer-reply specifically: `true` if source was copied. `false` if source was not copied.
     * @since 6.1
     */
    suspend fun copy(source: K, destination: K): Boolean?

    /**
     * Copy the value stored at the source key to the destination key.
     *
     * @param source the source.
     * @param destination the destination.
     * @param copyArgs the copyArgs.
     * @return Boolean integer-reply specifically: `true` if source was copied. `false` if source was not copied.
     * @since 6.1
     */
    suspend fun copy(source: K, destination: K, copyArgs: CopyArgs): Boolean?

    /**
     * Delete one or more keys.
     *
     * @param keys the keys.
     * @return Long integer-reply The number of keys that were removed.
     */
    suspend fun del(vararg keys: K): Long?

    /**
     * Delete the specified key if the compare condition matches.
     *
     * @param key the key.
     * @param compareCondition the compare condition, must not be `null`.
     * @return Long integer-reply the number of keys that were removed.
     * @since 7.1
     */
    @Experimental
    suspend fun delex(key: K, compareCondition: CompareCondition<V>): Long?

    /**
     * Return the XXH3 64-bit digest of the string value stored at a key as a 16-character hex string.
     *
     * @param key the key.
     * @return String bulk-string-reply the hex digest of the key's value, or `null` when `key` does not exist.
     * @since 7.1
     */
    @Experimental
    suspend fun digestKey(key: K): String?

    /**
     * Unlink one or more keys (non blocking DEL).
     *
     * @param keys the keys.
     * @return Long integer-reply The number of keys that were removed.
     */
    suspend fun unlink(vararg keys: K): Long?

    /**
     * Return a serialized version of the value stored at the specified key.
     *
     * @param key the key.
     * @return byte[] bulk-string-reply the serialized value.
     */
    suspend fun dump(key: K): ByteArray?

    /**
     * Determine how many keys exist.
     *
     * @param keys the keys.
     * @return Long integer-reply specifically: Number of existing keys.
     */
    suspend fun exists(vararg keys: K): Long?

    /**
     * Set a key's time to live in seconds.
     *
     * @param key the key.
     * @param seconds the seconds type: long.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set.
     */
    suspend fun expire(key: K, seconds: Long): Boolean?

    /**
     * Set a key's time to live in seconds.
     *
     * @param key the key.
     * @param seconds the seconds type: long.
     * @param expireArgs the expire arguments.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set.
     * @since 6.2
     */
    suspend fun expire(key: K, seconds: Long, expireArgs: ExpireArgs): Boolean?

    /**
     * Set a key's time to live in seconds.
     *
     * @param key the key.
     * @param seconds the seconds.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set.
     * @since 6.1
     */
    suspend fun expire(key: K, seconds: Duration): Boolean?

    /**
     * Set a key's time to live in seconds.
     *
     * @param key the key.
     * @param seconds the seconds.
     * @param expireArgs the expire arguments.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set.
     * @since 6.2
     */
    suspend fun expire(key: K, seconds: Duration, expireArgs: ExpireArgs): Boolean?

    /**
     * Set the expiration for a key as a UNIX timestamp.
     *
     * @param key the key.
     * @param timestamp the timestamp type: posix time.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set (see: `EXPIRE`).
     */
    suspend fun expireat(key: K, timestamp: Long): Boolean?

    /**
     * Set the expiration for a key as a UNIX timestamp.
     *
     * @param key the key.
     * @param timestamp the timestamp type: posix time.
     * @param expireArgs the expire arguments.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set (see: `EXPIRE`).
     * @since 6.2
     */
    suspend fun expireat(key: K, timestamp: Long, expireArgs: ExpireArgs): Boolean?

    /**
     * Set the expiration for a key as a UNIX timestamp.
     *
     * @param key the key.
     * @param timestamp the timestamp type: posix time.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set (see: `EXPIRE`).
     */
    suspend fun expireat(key: K, timestamp: Date): Boolean?

    /**
     * Set the expiration for a key as a UNIX timestamp.
     *
     * @param key the key.
     * @param timestamp the timestamp type: posix time.
     * @param expireArgs the expire arguments.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set (see: `EXPIRE`).
     * @since 6.2
     */
    suspend fun expireat(key: K, timestamp: Date, expireArgs: ExpireArgs): Boolean?

    /**
     * Set the expiration for a key as a UNIX timestamp.
     *
     * @param key the key.
     * @param timestamp the timestamp type: posix time.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set (see: `EXPIRE`).
     * @since 6.1
     */
    suspend fun expireat(key: K, timestamp: Instant): Boolean?

    /**
     * Set the expiration for a key as a UNIX timestamp.
     *
     * @param key the key.
     * @param timestamp the timestamp type: posix time.
     * @param expireArgs the expire arguments.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set (see: `EXPIRE`).
     * @since 6.2
     */
    suspend fun expireat(key: K, timestamp: Instant, expireArgs: ExpireArgs): Boolean?

    /**
     * Get the time to live for a key in as unix timestamp in seconds.
     *
     * @param key the key.
     * @return Long integer-reply in seconds, or a negative value in order to signal an error. The command returns `-1` if
     *         the key exists but has no associated expiration time. The command returns `-2` if the key does not exist.
     * @since 6.2
     */
    suspend fun expiretime(key: K): Long?

    /**
     * Find all keys matching the given pattern.
     *
     * @param pattern the pattern type.
     * @return List<K> array-reply list of keys matching `pattern`.
     */
    fun keys(pattern: String): Flow<K>

    /**
     * Find all keys matching the given pattern (legacy overload).
     *
     * @param pattern the pattern type: patternkey (pattern).
     * @return List&lt;K&gt; array-reply list of keys matching {@code pattern}.
     * @deprecated Use {@link #keys(String)} instead. This legacy overload will be removed in a later version.
     */
    @Deprecated("Use keys(String) instead. This legacy overload will be removed in a later version.")
    fun keysLegacy(pattern: K): Flow<K>

    /**
     * Atomically transfer a key from a Redis instance to another one.
     *
     * @param host the host.
     * @param port the port.
     * @param key the key.
     * @param db the database.
     * @param timeout the timeout in milliseconds.
     * @return String simple-string-reply The command returns OK on success.
     */
    suspend fun migrate(host: String, port: Int, key: K, db: Int, timeout: Long): String?

    /**
     * Atomically transfer one or more keys from a Redis instance to another one.
     *
     * @param host the host.
     * @param port the port.
     * @param db the database.
     * @param timeout the timeout in milliseconds.
     * @param migrateArgs migrate args that allow to configure further options.
     * @return String simple-string-reply The command returns OK on success.
     */
    suspend fun migrate(
        host: String,
        port: Int,
        db: Int,
        timeout: Long,
        migrateArgs: MigrateArgs<K>
    ): String?

    /**
     * Move a key to another database.
     *
     * @param key the key.
     * @param db the db type: long.
     * @return Boolean integer-reply specifically:.
     */
    suspend fun move(key: K, db: Int): Boolean?

    /**
     * Returns the kind of internal representation used in order to store the value associated with the `key`.
     *
     * @param key the key.
     * @return String.
     */
    suspend fun objectEncoding(key: K): String?

    /**
     * Returns the logarithmic access frequency counter of the object stored at the specified `key`.
     *
     * @param key the key.
     * @return Long.
     * @since 6.1
     */
    suspend fun objectFreq(key: K): Long?

    /**
     * Returns the number of seconds since the object stored at the specified key is idle (not requested by read or write
     * operations).
     *
     * @param key the key.
     * @return number of seconds since the object stored at the specified key is idle.
     */
    suspend fun objectIdletime(key: K): Long?

    /**
     * Returns the number of references of the value associated with the specified key.
     *
     * @param key the key.
     * @return Long.
     */
    suspend fun objectRefcount(key: K): Long?

    /**
     * Remove the expiration from a key.
     *
     * @param key the key.
     * @return Boolean integer-reply specifically:
     *
     *         `true` if the timeout was removed. `false` if `key` does not exist or does not have an
     *         associated timeout.
     */
    suspend fun persist(key: K): Boolean?

    /**
     * Set a key's time to live in milliseconds.
     *
     * @param key the key.
     * @param milliseconds the milliseconds type: long.
     * @return integer-reply, specifically: `true` if the timeout was set. `false` if `key` does not exist or
     *         the timeout could not be set.
     */
    suspend fun pexpire(key: K, milliseconds: Long): Boolean?

    /**
     * Set a key's time to live in milliseconds.
     *
     * @param key the key.
     * @param milliseconds the milliseconds type: long.
     * @param expireArgs the expire arguments.
     * @return integer-reply, specifically: `true` if the timeout was set. `false` if `key` does not exist or
     *         the timeout could not be set.
     * @since 6.2
     */
    suspend fun pexpire(key: K, milliseconds: Long, expireArgs: ExpireArgs): Boolean?

    /**
     * Set a key's time to live in milliseconds.
     *
     * @param key the key.
     * @param milliseconds the milliseconds.
     * @return integer-reply, specifically: `true` if the timeout was set. `false` if `key` does not exist or
     *         the timeout could not be set.
     * @since 6.1
     */
    suspend fun pexpire(key: K, milliseconds: Duration): Boolean?

    /**
     * Set a key's time to live in milliseconds.
     *
     * @param key the key.
     * @param milliseconds the milliseconds.
     * @param expireArgs the expire arguments.
     * @return integer-reply, specifically: `true` if the timeout was set. `false` if `key` does not exist or
     *         the timeout could not be set.
     * @since 6.2
     */
    suspend fun pexpire(key: K, milliseconds: Duration, expireArgs: ExpireArgs): Boolean?

    /**
     * Set the expiration for a key as a UNIX timestamp specified in milliseconds.
     *
     * @param key the key.
     * @param timestamp the milliseconds-timestamp type: posix time.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set (see: `EXPIRE`).
     */
    suspend fun pexpireat(key: K, timestamp: Long): Boolean?

    /**
     * Set the expiration for a key as a UNIX timestamp specified in milliseconds.
     *
     * @param key the key.
     * @param timestamp the milliseconds-timestamp type: posix time.
     * @param expireArgs the expire arguments.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set (see: `EXPIRE`).
     * @since 6.2
     */
    suspend fun pexpireat(key: K, timestamp: Long, expireArgs: ExpireArgs): Boolean?

    /**
     * Set the expiration for a key as a UNIX timestamp specified in milliseconds.
     *
     * @param key the key.
     * @param timestamp the milliseconds-timestamp type: posix time.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set (see: `EXPIRE`).
     */
    suspend fun pexpireat(key: K, timestamp: Date): Boolean?

    /**
     * Set the expiration for a key as a UNIX timestamp specified in milliseconds.
     *
     * @param key the key.
     * @param timestamp the milliseconds-timestamp type: posix time.
     * @param expireArgs the expire arguments.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set (see: `EXPIRE`).
     * @since 6.2
     */
    suspend fun pexpireat(key: K, timestamp: Date, expireArgs: ExpireArgs): Boolean?

    /**
     * Set the expiration for a key as a UNIX timestamp specified in milliseconds.
     *
     * @param key the key.
     * @param timestamp the milliseconds-timestamp type: posix time.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set (see: `EXPIRE`).
     */
    suspend fun pexpireat(key: K, timestamp: Instant): Boolean?

    /**
     * Set the expiration for a key as a UNIX timestamp specified in milliseconds.
     *
     * @param key the key.
     * @param timestamp the milliseconds-timestamp type: posix time.
     * @param expireArgs the expire arguments.
     * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not
     *         exist or the timeout could not be set (see: `EXPIRE`).
     * @since 6.2
     */
    suspend fun pexpireat(key: K, timestamp: Instant, expireArgs: ExpireArgs): Boolean?

    /**
     * Get the time to live for a key in as unix timestamp in milliseconds.
     *
     * @param key the key.
     * @return Long integer-reply in milliseconds, or a negative value in order to signal an error. The command returns
     *         `-1` if the key exists but has no associated expiration time. The command returns `-2` if the key
     *         does not exist.
     * @since 6.2
     */
    suspend fun pexpiretime(key: K): Long?

    /**
     * Get the time to live for a key in milliseconds.
     *
     * @param key the key.
     * @return Long integer-reply in milliseconds, or a negative value in order to signal an error. The command returns
     *         `-1` if the key exists but has no associated expiration time. The command returns `-2` if the key
     *         does not exist.
     */
    suspend fun pttl(key: K): Long?

    /**
     * Return a random key from the keyspace.
     *
     * @return K bulk-string-reply the random key, or `null` when the database is empty.
     */
    suspend fun randomkey(): K?

    /**
     * Rename a key.
     *
     * @param key the key.
     * @param newKey the newkey type: key.
     * @return String simple-string-reply.
     */
    suspend fun rename(key: K, newKey: K): String?

    /**
     * Rename a key, only if the new key does not exist.
     *
     * @param key the key.
     * @param newKey the newkey type: key.
     * @return Boolean integer-reply specifically:
     *
     *         `true` if `key` was renamed to `newkey`. `false` if `newkey` already exists.
     */
    suspend fun renamenx(key: K, newKey: K): Boolean?

    /**
     * Create a key using the provided serialized value, previously obtained using DUMP.
     *
     * @param key the key.
     * @param ttl the ttl type: long.
     * @param value the serialized-value type: string.
     * @return String simple-string-reply The command returns OK on success.
     */
    suspend fun restore(key: K, ttl: Long, value: ByteArray): String?

    /**
     * Create a key using the provided serialized value, previously obtained using DUMP.
     *
     * @param key the key.
     * @param value the serialized-value type: string.
     * @param args the [RestoreArgs], must not be `null`.
     * @return String simple-string-reply The command returns OK on success.
     * @since 5.1
     */
    suspend fun restore(key: K, value: ByteArray, args: RestoreArgs): String?

    /**
     * Sort the elements in a list, set or sorted set.
     *
     * @param key the key.
     * @return List<V> array-reply list of sorted elements.
     */
    fun sort(key: K): Flow<V>

    /**
     * Sort the elements in a list, set or sorted set.
     *
     * @param key the key.
     * @param sortArgs sort arguments.
     * @return List<V> array-reply list of sorted elements.
     */
    fun sort(key: K, sortArgs: SortArgs): Flow<V>

    /**
     * Sort the elements in a list, set or sorted set.
     *
     * @param key the key.
     * @return List<V> array-reply list of sorted elements.
     * @since 6.2
     */
    fun sortReadOnly(key: K): Flow<V>

    /**
     * Sort the elements in a list, set or sorted set.
     *
     * @param key the key.
     * @param sortArgs sort arguments.
     * @return List<V> array-reply list of sorted elements.
     * @since 6.2
     */
    fun sortReadOnly(key: K, sortArgs: SortArgs): Flow<V>

    /**
     * Sort the elements in a list, set or sorted set.
     *
     * @param key the key.
     * @param sortArgs sort arguments.
     * @param destination the destination key to store sort results.
     * @return Long number of values.
     */
    suspend fun sortStore(key: K, sortArgs: SortArgs, destination: K): Long?

    /**
     * Touch one or more keys. Touch sets the last accessed time for a key. Non-exsitent keys wont get created.
     *
     * @param keys the keys.
     * @return Long integer-reply the number of found keys.
     */
    suspend fun touch(vararg keys: K): Long?

    /**
     * Get the time to live for a key.
     *
     * @param key the key.
     * @return Long integer-reply TTL in seconds, or a negative value in order to signal an error. The command returns
     *         `-1` if the key exists but has no associated expiration time. The command returns `-2` if the key
     *         does not exist.
     */
    suspend fun ttl(key: K): Long?

    /**
     * Determine the type stored at key.
     *
     * @param key the key.
     * @return String simple-string-reply type of `key`, or `none` when `key` does not exist.
     */
    suspend fun type(key: K): String?

    /**
     * Incrementally iterate the keys space.
     *
     * @return KeyScanCursor<K> scan cursor.
     */
    suspend fun scan(): KeyScanCursor<K>?

    /**
     * Incrementally iterate the keys space. Use [KeyScanArgs] to specify `SCAN`-specific arguments.
     *
     * @param scanArgs scan arguments.
     * @return KeyScanCursor<K> scan cursor.
     * @see KeyScanArgs
     */
    suspend fun scan(scanArgs: ScanArgs): KeyScanCursor<K>?

    /**
     * Incrementally iterate the keys space. Use [KeyScanArgs] to specify `SCAN`-specific arguments.
     *
     * @param scanCursor cursor to resume from a previous scan, must not be `null`.
     * @param scanArgs scan arguments.
     * @return KeyScanCursor<K> scan cursor.
     * @see KeyScanArgs
     */
    suspend fun scan(scanCursor: ScanCursor, scanArgs: ScanArgs): KeyScanCursor<K>?

    /**
     * Incrementally iterate the keys space.
     *
     * @param scanCursor cursor to resume from a previous scan, must not be `null`.
     * @return KeyScanCursor<K> scan cursor.
     */
    suspend fun scan(scanCursor: ScanCursor): KeyScanCursor<K>?

}

